#ifndef MANIFOLD_KERNEL_SCHEDULER_H
#define MANIFOLD_KERNEL_SCHEDULER_H

#include <set>

#include "manifold-event.h"

namespace manifold {
namespace kernel {


class event_less
{
public:
  event_less() { }
  inline bool operator()(EventBase* const & l, const EventBase* const & r) const {
    if(l->time < r->time) return true;
    if (l->time == r->time) return l->uid < r->uid;
    return false;
  }
};


class Scheduler {
public:
    Scheduler();
    virtual ~Scheduler() {}

    virtual void Run() = 0;
    void Stop() { m_halted = true; }

    virtual bool isTicked() = 0;
    virtual bool isTimed() = 0;
    virtual void scheduleTimedEvent(EventBase*) = 0;
    virtual bool cancelTimedEvent(EventId&) = 0;
    virtual EventId peek() = 0; //return ID of 1st timed event.
    virtual EventBase* GetEarliestEvent() = 0; //return 1st timed event
    Time_t get_simTime() { return m_simTime; }

    virtual void print_stats(std::ostream&);

#ifdef KERNEL_UTEST
    void unhalt() { m_halted = false; }
#ifndef NO_MPI
    Time_t get_grantedTime() { return grantedTime; }
    bool call_isSafeToProcess(double t) { return isSafeToProcess(t); }
#endif
#endif
protected:
    //????????????????????? the following should be moved to the SyncAlgorithm class.
    #ifndef NO_MPI
    void handle_incoming_messages();

    struct LBTS_Msg {
	int epoch;
	int tx_count;
	int rx_count;
	int myId;
	Time_t smallest_time;
    };

    bool isSafeToProcess(double requestTime);
    Time_t grantedTime; //used by LBTS
    #endif

    bool m_halted;
    Time_t m_simTime; //current simulation time


    typedef std::set<EventBase*, event_less> EventSet_t;

    unsigned long stats_msg_received;
    unsigned long stats_LBTS_sync;
};




//! When all the events in the system are clock tick based, this
//! scheduler should be used.
class TickedScheduler : public Scheduler {
public:
    TickedScheduler() {}
    ~TickedScheduler() {}

    void Run();
    bool isTicked() { return true; }
    bool isTimed() { return false; }
    void scheduleTimedEvent(EventBase*);
    bool cancelTimedEvent(EventId&);
    EventId peek(); //return ID of 1st timed event.
    EventBase* GetEarliestEvent(); //return 1st timed event
    //?????????????????????????????????? should clocks be member of the scheduler?

};




//! When none of the events in the system are clock tick based, this
//! scheduler should be used.
class TimedScheduler : public Scheduler {
public:
    TimedScheduler() {}
    ~TimedScheduler() {}
    void Run();
    bool isTicked() { return false; }
    bool isTimed() { return true; }
    void scheduleTimedEvent(EventBase*);
    bool cancelTimedEvent(EventId&);
    EventId peek(); //return ID of 1st timed event.
    EventBase* GetEarliestEvent(); //return 1st timed event
private:
    EventSet_t m_timedEvents;
};




//! When events in the system are a mixture of tick based and non-tick-based,
//! This scheduler should be used.
class MixedScheduler : public Scheduler {
public:
    MixedScheduler() {}
    ~MixedScheduler() {}
    void Run();
    bool isTicked() { return true; }
    bool isTimed() { return true; }
    void scheduleTimedEvent(EventBase*);
    bool cancelTimedEvent(EventId&);
    EventId peek(); //return ID of 1st timed event.
    EventBase* GetEarliestEvent(); //return 1st timed event
private:
    EventSet_t m_timedEvents;
};



} //kernel
} //manifold

#endif // MANIFOLD_KERNEL_SCHEDULER_H
